/* $RCSFile: FCKconnectorV2.aspx.cs $
 * Apache license v2 2011 by Christian Surieux
 * $Source: ?/AddonNice/app_support/FCKconnectorV2.aspx.cs $
 * $log$
 * Revision 20 2010/08/09 10:51:18  christian.surieux
 *   Added Log comment to all .aspx .ascx .master files
 * Revision 19 2010/08/09 09:43:42  christian.surieux
 *   Added Log to cs sources files
 * Revision 6 2010/08/06 19:56:24  christian.surieux
 *   Added Log to All cs files
 */

/*
 * Based on original FileBrowserConnector.cs from Frederico Caldeira Knabben (fredck@fckeditor.net)
 */

using System;
using System.Runtime.InteropServices ;
using System.Globalization;
using System.Xml;
using System.IO;
using System.Web;
using System.Collections;
using System.Collections.Generic;
using AddonNice.UI;
using AddonNice.Configuration;
using AddonNice.Security;
using AddonNice.Diagnostics;

using Path2 =   AddonNice.Settings.Path;

namespace AddonNice.FCKeditorV2
{
	public partial class FileBrowserConnector : ADNPage //.EditItemPage //System.Web.UI.Page
	{
		private string sUserFilesPath ;
		private string sUserFilesDirectory ;

		protected override void LoadSettings()
		{
		}

		protected override void OnLoad(EventArgs e)
		{
			System.Diagnostics.Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"FCKConnectorV2 OnLoad Begin at: {0}", DateTime.UtcNow));
			// Get the main request informaiton.
			string sCommand = Request.QueryString["Command"];
			if (sCommand == null)
				return;
            // CSU 24/06/08 potential pb if  page in MS CRM iframe  because CRM uses same querystring
			string sResourceType = Request.QueryString["Type"];
			if (sResourceType == null)
				return;

			string sCurrentFolder = Request.QueryString["CurrentFolder"];
			if (sCurrentFolder == null)
				return;
			try
			{

				// Check the current folder syntax (must begin and start with a slash).
				if (!sCurrentFolder.EndsWith("/"))
					sCurrentFolder += "/";
				if (!sCurrentFolder.StartsWith("/"))
					sCurrentFolder = "/" + sCurrentFolder;

				// File Upload doesn't have to return XML, so it must be intercepted before anything.
				if (sCommand == "FileUpload")
				{
					this.FileUpload(sResourceType, sCurrentFolder);
					return;
				}

				// Cleans the response buffer.
				Response.ClearHeaders();
				Response.Clear();

				// Prevent the browser from caching the result.
				Response.CacheControl = "no-cache";

				// Set the response format.
				Response.ContentEncoding = System.Text.UTF8Encoding.UTF8;
				Response.ContentType = "text/xml";

				XmlDocument oXML = new XmlDocument();
				XmlNode oConnectorNode = CreateBaseXml(oXML, sCommand, sResourceType, sCurrentFolder);

				// Execute the required command.
				switch (sCommand)
				{
					case "GetFolders":
						this.GetFolders(oConnectorNode, sResourceType, sCurrentFolder);
						break;
					case "GetFoldersAndFiles":
						this.GetFolders(oConnectorNode, sResourceType, sCurrentFolder);
						this.GetFiles(oConnectorNode, sResourceType, sCurrentFolder);
						break;
					case "CreateFolder":
						this.CreateFolder(oConnectorNode, sResourceType, sCurrentFolder);
						break;
				}

				// Output the resulting XML.
				Response.Write(oXML.OuterXml);
			}
			catch (Exception ex)
			{
				System.Diagnostics.Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"FCKConnectorV2 OnLoad sResourceType: {0}, sCurrentFolder: {1}, ex : {2}", sResourceType, sCurrentFolder, ex));
			}
			System.Diagnostics.Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"FCKConnectorV2 OnLoad End at: {0}", DateTime.UtcNow));
			Response.End() ;
		}

		#region Base XML Creation

		private XmlNode CreateBaseXml( XmlDocument xml, string command, string resourceType, string currentFolder )
		{
			// Create the XML document header.
			xml.AppendChild( xml.CreateXmlDeclaration( "1.0", "utf-8", null ) ) ;

			// Create the main "Connector" node.
			XmlNode oConnectorNode = XmlUtil.AppendElement( xml, "Connector" ) ;
			XmlUtil.SetAttribute( oConnectorNode, "command", command ) ;
			XmlUtil.SetAttribute( oConnectorNode, "resourceType", resourceType ) ;

			// Add the current folder node.
			XmlNode oCurrentNode = XmlUtil.AppendElement( oConnectorNode, "CurrentFolder" ) ;
			XmlUtil.SetAttribute( oCurrentNode, "path", currentFolder ) ;
			XmlUtil.SetAttribute( oCurrentNode, "url", GetUrlFromPath( resourceType, currentFolder) ) ;

			return oConnectorNode ;
		}

		#endregion

		#region Command Handlers

		private void GetFolders( XmlNode connectorNode, string resourceType, string currentFolder )
		{
			try
			{
				// Map the virtual path to the local server path.
				string sServerDir = this.ServerMapFolder(resourceType, currentFolder);

				// Create the "Folders" node.
				XmlNode oFoldersNode = XmlUtil.AppendElement(connectorNode, "Folders");

				System.IO.DirectoryInfo oDir = new System.IO.DirectoryInfo(sServerDir);
				System.IO.DirectoryInfo[] aSubDirs = oDir.GetDirectories();

				for (int i = 0; i < aSubDirs.Length; i++)
				{
					// Create the "Folders" node.
					XmlNode oFolderNode = XmlUtil.AppendElement(oFoldersNode, "Folder");
					XmlUtil.SetAttribute(oFolderNode, "name", aSubDirs[i].Name);
				}
			}
			catch (Exception ex)
			{
				System.Diagnostics.Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"FCKConnectorV2 GetFolders resourceType: {0}, currentFolder: {1}, ex : {2}", resourceType, currentFolder, ex));
			}
		}

		private void GetFiles( XmlNode connectorNode, string resourceType, string currentFolder )
		{
			try
			{
				// Map the virtual path to the local server path.
				string sServerDir = this.ServerMapFolder(resourceType, currentFolder);

				// Create the "Files" node.
				XmlNode oFilesNode = XmlUtil.AppendElement(connectorNode, "Files");

				System.IO.DirectoryInfo oDir = new System.IO.DirectoryInfo(sServerDir);
				System.IO.FileInfo[] aFiles = oDir.GetFiles();

				for (int i = 0; i < aFiles.Length; i++)
				{
					Decimal iFileSize = Math.Round((Decimal)aFiles[i].Length / 1024);
					if (iFileSize < 1 && aFiles[i].Length != 0) iFileSize = 1;

					// Create the "File" node.
					XmlNode oFileNode = XmlUtil.AppendElement(oFilesNode, "File");
					XmlUtil.SetAttribute(oFileNode, "name", aFiles[i].Name);
					XmlUtil.SetAttribute(oFileNode, "size", iFileSize.ToString(CultureInfo.InvariantCulture));
				}
			}
			catch (Exception ex)
			{
				System.Diagnostics.Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"FCKConnectorV2 GetFiles resourceType: {0}, currentFolder: {1}, ex : {2}",resourceType, currentFolder,ex));
			}
		}

		private void CreateFolder( XmlNode connectorNode, string resourceType, string currentFolder )
		{
			string sErrorNumber = "0" ;

			string sNewFolderName = Request.QueryString["NewFolderName"] ;

			if ( sNewFolderName == null || sNewFolderName.Length == 0 )
				sErrorNumber = "102" ;
			else
			{
				// Map the virtual path to the local server path of the current folder.
				string sServerDir = this.ServerMapFolder( resourceType, currentFolder ) ;

				try
				{
					Util.CreateDirectory( System.IO.Path.Combine( sServerDir, sNewFolderName )) ;
				}
				catch ( ArgumentException )
				{
					sErrorNumber = "102" ;
				}
				catch ( System.IO.PathTooLongException )
				{
					sErrorNumber = "102" ;
				}
				catch ( System.IO.IOException )
				{
					sErrorNumber = "101" ;
				}
				catch ( System.Security.SecurityException )
				{
					sErrorNumber = "103" ;
				}
				catch ( Exception )
				{
					sErrorNumber = "110" ;
				}
			}

			// Create the "Error" node.
			XmlNode oErrorNode = XmlUtil.AppendElement( connectorNode, "Error" ) ;
			XmlUtil.SetAttribute( oErrorNode, "number", sErrorNumber ) ;
		}

		private void FileUpload( string resourceType, string currentFolder )
		{
			try
			{
				HttpPostedFile oFile = Request.Files["NewFile"];

				string sErrorNumber = "0";
				string sFileName = string.Empty;

				if (oFile != null)
				{
					// Map the virtual path to the local server path.
					string sServerDir = this.ServerMapFolder(resourceType, currentFolder);

					// Get the uploaded file name.
					sFileName = System.IO.Path.GetFileName(oFile.FileName);

					int iCounter = 0;

					while (true)
					{
						string sFilePath = System.IO.Path.Combine(sServerDir, sFileName);

						if (System.IO.File.Exists(sFilePath))
						{
							iCounter++;
							sFileName =
								System.IO.Path.GetFileNameWithoutExtension(oFile.FileName) +
								"(" + iCounter + ")" +
								System.IO.Path.GetExtension(oFile.FileName);

							sErrorNumber = "201";
						}
						else
						{
							oFile.SaveAs(sFilePath);
							break;
						}
					}
				}
				else
					sErrorNumber = "202";

				Response.Clear();

				Response.Write("<script type=\"text/javascript\">");
				Response.Write("window.parent.frames['frmUpload'].OnUploadCompleted(" + sErrorNumber + ",'" + sFileName.Replace("'", "\\'") + "') ;");
				Response.Write("</script>");
			}
			catch (Exception ex)
			{
				System.Diagnostics.Trace.WriteLine( string.Format(CultureInfo.InvariantCulture,"FCKConnectorV2 FileUpload resourceType: {0}, currentFolder: {1}, ex : {2}", resourceType, currentFolder, ex));
			}

			Response.End() ;
		}

		#endregion

		#region Directory Mapping

		private string ServerMapFolder( string resourceType, string folderPath )
		{
			// Ensure that the directory exists.
			Util.CreateDirectory( this.UserFilesDirectory ) ;

			// Return the resource type directory combined with the required path.
			return System.IO.Path.Combine( this.UserFilesDirectory, folderPath.TrimStart('/') ) ;
		}

		private string GetUrlFromPath( string resourceType, string folderPath )
		{
			if ( resourceType == null || resourceType.Length == 0 )
				return this.UserFilesPath.TrimEnd('/') + folderPath ;
			else
				return this.UserFilesPath + folderPath ;
		}

		private string UserFilesPath
		{
			get
			{
				if ( sUserFilesPath == null )
				{
					PortalSettings portalSettings   =   (PortalSettings) HttpContext.Current.Items[StStr.strPortalSettings];
					if (portalSettings == null) 
						return UserFilesPath2;
                    ModulesDB mdb                   =   new ModulesDB();
                    int xmoduleID                   =   PortalSettings.TheActiveModule;
                    string val                      =   mdb.GetSingleSettings(xmoduleID,StStr.MODULE_IMAGE_FOLDER);
					
					string DefaultImageFolder       =   StStr.DefaultImageFolder;
					if ( val != string.Empty ) 
					{
						DefaultImageFolder          =   val;
					}
					else 
					{
						DefaultImageFolder          =   portalSettings.CustomSettings[StStr.SiteSettingsDefaultImageFolder].Value;
					}
					sUserFilesPath                  =   Path2.WebPathCombine(portalSettings.PortalFullPath, "images", DefaultImageFolder);
				}
				return sUserFilesPath ;
			}
		}

		private const string DEFAULT_USER_FILES_PATH = "/UserFiles/" ;

		protected string UserFilesPath2
		{
			get
			{
				if ( sUserFilesPath == null )
				{
					// Try to get from the "Application".
					sUserFilesPath = (string)Application["FCKeditor:UserFilesPath"] ;

					// Try to get from the "Session".
					if ( sUserFilesPath == null || sUserFilesPath.Length == 0 )
					{
						sUserFilesPath = (string)Session["FCKeditor:UserFilesPath"] ;
						
						// Try to get from the Web.config file.
						if ( sUserFilesPath == null || sUserFilesPath.Length == 0 )
						{
							sUserFilesPath = System.Configuration.ConfigurationManager.AppSettings["FCKeditor:UserFilesPath"] ;
							
							// Try to get from the URL.
							if ( sUserFilesPath == null || sUserFilesPath.Length == 0 ) 
							{
								sUserFilesPath = Request.QueryString["ServerPath"] ;
							}
							// Otherwise use the default value.
							if ( sUserFilesPath == null || sUserFilesPath.Length == 0 ) 
								sUserFilesPath = DEFAULT_USER_FILES_PATH ;
						}
					}

					// Check that the user path ends with slash ("/")
					if ( ! sUserFilesPath.EndsWith("/") )
						sUserFilesPath += "/" ;
				}
				return sUserFilesPath ;
			}
		}

		private string UserFilesDirectory
		{
			get	
			{
				if ( sUserFilesDirectory == null )
				{
					// Get the local (server) directory path translation.
					sUserFilesDirectory = Server.MapPath( this.UserFilesPath ) ;
				}
				return sUserFilesDirectory ;
			}
		}

		#endregion
	}

	public sealed class Util
	{
		// The "_mkdir" function is used by the "CreateDirectory" method.
		[DllImport("msvcrt.dll", SetLastError=true)]
		private static extern int _mkdir(string path) ;

		private Util()
		{}

		/// <summary>
		/// This method should provide safe substitude for Directory.CreateDirectory().
		/// </summary>
		/// <param name="path">The directory path to be created.</param>
		/// <returns>A <see cref="System.IO.DirectoryInfo"/> object for the created directory.</returns>
		/// <remarks>
		///		<para>
		///		This method creates all the directory structure if needed.
		///		</para>
		///		<para>
		///		The System.IO.Directory.CreateDirectory() method has a bug that gives an
		///		error when trying to create a directory and the user has no rights defined
		///		in one of its parent directories. The CreateDirectory() should be a good 
		///		replacement to solve this problem.
		///		</para>
		/// </remarks>
		public static DirectoryInfo CreateDirectory( string path )
		{
			ArrayList oDirsToCreate = new ArrayList() ;

			// Create the directory info object for that dir (normalized to its absolute representation).
			DirectoryInfo oDir = new DirectoryInfo( Path.GetFullPath( path ) ) ;

			// Check the entire path structure to find directories that must be created.
			while ( oDir != null && !oDir.Exists )
			{
				oDirsToCreate.Add( oDir.FullName ) ;
				oDir = oDir.Parent ;
			}

			// "oDir == null" means that the check arrives in the root and it doesn't exist too.
			if ( oDir == null )
				throw( new System.IO.DirectoryNotFoundException( "Directory \"" + oDirsToCreate[ oDirsToCreate.Count-1 ] + "\" not found." ) ) ;

			// Create all directories that must be created (from bottom to top).
			for( int i = oDirsToCreate.Count - 1 ; i >= 0 ; i-- )
			{
				string sPath = (string)oDirsToCreate[i] ;
				int iReturn = _mkdir( sPath ) ;

				if ( iReturn != 0 )
					throw new ApplicationException("Error calling [msvcrt.dll]:_wmkdir(" + sPath + "), error code: " + iReturn );
			}

			return new DirectoryInfo( path ) ;
		}
	}

	internal sealed class XmlUtil
	{
		private XmlUtil()
		{}

		public static XmlNode AppendElement( XmlNode node, string newElementName )
		{
			return AppendElement( node, newElementName, null ) ;
		}

		public static XmlNode AppendElement( XmlNode node, string newElementName, string innerValue )
		{
			XmlNode oNode ;

			if ( node is XmlDocument )
				oNode = node.AppendChild( ((XmlDocument)node).CreateElement( newElementName ) ) ;
			else
				oNode = node.AppendChild( node.OwnerDocument.CreateElement( newElementName ) ) ;

			if ( innerValue != null )
				oNode.AppendChild( node.OwnerDocument.CreateTextNode( innerValue ) ) ;

			return oNode ;
		}

		public static XmlAttribute CreateAttribute( XmlDocument xmlDocument, string name, string value )
		{
			XmlAttribute oAtt = xmlDocument.CreateAttribute( name ) ;
			oAtt.Value = value ;
			return oAtt ;
		}

		public static void SetAttribute( XmlNode node, string attributeName, string attributeValue )
		{
			if ( node.Attributes[ attributeName ] != null )
				node.Attributes[ attributeName ].Value = attributeValue ;
			else
				node.Attributes.Append( CreateAttribute( node.OwnerDocument, attributeName, attributeValue ) ) ;
		}
	}

}
